home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / C / Games / Xconq 7.0d16 / Xconq 7.0d16 src / mac / macinit.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-12-16  |  61.2 KB  |  2,160 lines  |  [TEXT/KAHL]

  1. /* Copyright (c) 1992, 1993  Stanley T. Shebs. */
  2. /* This program may be used, copied, modified, and redistributed freely */
  3. /* for noncommercial purposes, so long as this notice remains intact. */
  4.  
  5. /* Initialization of the Mac interface. */
  6.  
  7. #include "conq.h"
  8. #include "mac.h"
  9.  
  10. #define MAXCHECKBOXES 10
  11. #define MAXSLIDERS 3
  12.  
  13. #define toggle_checkbox(ih) \
  14.   SetCtlValue((ControlHandle) (ih), !GetCtlValue((ControlHandle) (ih)))
  15.  
  16. /* (not quite right - come up with a better test) */
  17.  
  18. #define game_already_loaded() (numutypes > 0)
  19.  
  20. #define t_color_defined(t) (hasColorQD && tcolors != NULL && tcolors[t] != NULL)
  21.  
  22. #undef p2c
  23. #define p2c(pstr,buf)  \
  24.   strncpy(buf, pstr+1, pstr[0]);  \
  25.   /* Make sure this name is terminated properly. */  \
  26.   buf[pstr[0]] = '\0';
  27.  
  28. extern Assign *assignments;
  29. extern int foundimagesfile;
  30. extern int allbedesigners;
  31.  
  32. char *copy_string();
  33.  
  34. BitMap *bordbitmaps;
  35. BitMap *connbitmaps;
  36.  
  37. char *firstgamename = "intro"; /* into a special str resource?? */
  38.  
  39. int varyseen;
  40. int varyallseen;
  41. int varyworld;
  42. int anyvariants;
  43.  
  44. /* This is the dialog used to select a game to play. */
  45.  
  46. WindowPtr newgamewin = nil;
  47.  
  48. ListHandle newgamelist = nil;
  49.  
  50. TEHandle newgametext = nil;
  51.  
  52. PicHandle newgamepicture = nil;
  53.  
  54. Module **possiblegames = NULL;
  55.  
  56. int numgames = 0;
  57.  
  58. Module *selectedgame = NULL;
  59.  
  60. /* This is the dialog used to set the size and shape of the world. */
  61.  
  62. DialogPtr worldshapewin = nil;
  63.  
  64. /* These are the values that will be used to set world geometry. */
  65.  
  66. long newcircumference;
  67. long newwidth;
  68. long newheight;
  69. long newlatitude;
  70. long newlongitude;
  71. long newxwrap;
  72.  
  73. int newseen;
  74. int newseeall;
  75.  
  76. /* This is the variant-setting dialog. */
  77.  
  78. DialogPtr variantswin = nil;
  79.  
  80. int numcheckboxes;
  81. int numsliders;
  82.  
  83. Obj *checkboxes[MAXCHECKBOXES];
  84. char *checkboxnames[MAXCHECKBOXES];
  85. Obj *sliders[MAXSLIDERS];
  86. char *slidernames[MAXSLIDERS];
  87.  
  88. Obj *variants;
  89.  
  90. /* This is the player setup dialog. */
  91.  
  92. WindowPtr playersetupwin = nil;
  93.  
  94. PicHandle updownpicture = nil;
  95. PicHandle updownpictureup = nil;
  96. PicHandle updownpicturedown = nil;
  97.  
  98. int playerh = 22;
  99. int playerbaseline = 14;
  100.  
  101. int selectedplayer = -1;
  102.  
  103. #define selected_a_player() (between(0, selectedplayer, numsides - 1))
  104.  
  105. #define selected_player()  \
  106.   (selected_a_player() ? assignments[selectedplayer].player : NULL)
  107.  
  108. #define selected_side() (selected_a_player() ? assignments[selectedplayer].side : NULL)
  109.  
  110. ImageFamily *uimages = NULL;
  111. ImageFamily *timages = NULL;
  112. ImageFamily *eimages = NULL;
  113.  
  114. ImageColor **tcolors = NULL;
  115.  
  116. CursHandle readprogressors[NUMcParens];
  117. CursHandle progressors[NUMcSynth];
  118. CursHandle movecursors[NUMcMoves];
  119. CursHandle nomovecursor;
  120. CursHandle allmovecursor;
  121. CursHandle grayarrowcursor;
  122. CursHandle opencrosscursor;
  123. CursHandle firecursor;
  124. CursHandle watchcursor;
  125.  
  126. /* This value is the current progress cursor. */
  127.  
  128. int curcurs = 0;
  129.  
  130. /* The initialization progress dialog. */
  131.  
  132. DialogPtr progresswin = nil;
  133.  
  134. /* The current value of the percent progress. */
  135.  
  136. int progress;
  137.  
  138. /* The previous value of the percent progress. */
  139.  
  140. int lastprogress;
  141.  
  142. enum grays gridgray = whitegray;
  143. enum grays unseengray = whitegray;
  144. enum grays bggray = mediumgray;
  145.  
  146. RGBColor gridcolor;
  147. RGBColor unseencolor;
  148. RGBColor bgcolor;
  149. RGBColor blackcolor;
  150.  
  151. int gridmatchesunseen = FALSE;
  152.  
  153. PicHandle dotdotdotpicture = nil;
  154.  
  155. /* Set up the various cursors we'll be using. */
  156.  
  157. init_cursors()
  158. {
  159.     int i;
  160.  
  161.     /* Get all the cursors used to indicate reader progress. */
  162.     for (i = 0; i < NUMcParens; ++i) {
  163.         readprogressors[i] = GetCursor(cParens1 + i);
  164.     }
  165.     /* Get all the cursors used to indicate synthesis progress. */
  166.     for (i = 0; i < NUMcSynth; ++i) {
  167.         progressors[i] = GetCursor(cSynth1 + i);
  168.     }
  169.     /* Get all the cursors indicating the move direction. */
  170.     for (i = 0; i < NUMcMoves; ++i) {
  171.         movecursors[i] = GetCursor(cMove1 + i);
  172.     }
  173.     /* Miscellaneous cursors. */
  174.     nomovecursor = GetCursor(cNoMove);
  175.     allmovecursor = GetCursor(cAllMove);
  176.     grayarrowcursor = GetCursor(cGrayArrow);
  177.     opencrosscursor = GetCursor(cOpenCross);
  178.     firecursor = GetCursor(138);
  179.     watchcursor = GetCursor(watchCursor);
  180.     /* Designer-related cursors will be done later, if needed at all. */
  181.  
  182.     dotdotdotpicture = (PicHandle) GetResource('PICT', 134);
  183.  
  184. }
  185.  
  186. pascal Boolean
  187. filter_splash(dialog, evt, itemhit)
  188. DialogPtr dialog;
  189. EventRecord *evt;
  190. short *itemhit;
  191. {
  192.     GrafPtr oldport;
  193.     Point pt;
  194.     short ditem;
  195.     char ch;
  196.  
  197.     /* Look for the right kind of event. */
  198.     switch (evt->what) {
  199.         case keyDown:
  200.             ch = evt->message & charCodeMask;
  201.             if (ch == 3 || ch == 13) {
  202.                 *itemhit = diSplashNew;
  203.                 return TRUE;
  204.             }
  205. #ifdef DEBUGGING
  206.             /* A secret way to get debugging at startup. */
  207.             /* (should have some sort of feedback?) */
  208.             if (ch == 'D') {
  209.                 toggle_debugging(&Debug);
  210.             }
  211.             if (ch == 'M') {
  212.                 toggle_debugging(&DebugM);
  213.             }
  214.             if (ch == 'G') {
  215.                 toggle_debugging(&DebugG);
  216.             }
  217. #endif
  218.             break;
  219.     }
  220.     return FALSE;
  221. }
  222.  
  223. /* Display the initial splash screen, and let the player choose New/Open/Connect/Quit. */
  224.  
  225. do_splash_box()
  226. {
  227.     short ditem;
  228.     Str255 tmpstr;
  229.     WindowPtr win;
  230.     PicHandle pic;
  231.     short itemtype;  Handle itemhandle;  Rect itemrect;
  232.  
  233.     win = GetNewDialog(dSplash, NULL, (DialogPtr) -1L);
  234.     /* Fill in the kernel's version and copyright. */
  235.     GetDItem(win, diSplashVersion, &itemtype, &itemhandle, &itemrect);
  236.     c2p(version_string(), tmpstr);
  237.     SetIText(itemhandle, tmpstr);
  238.     GetDItem(win, diSplashCopyright, &itemtype, &itemhandle, &itemrect);
  239.     c2p(copyright_string(), tmpstr);
  240.     SetIText(itemhandle, tmpstr);
  241.     /* Substitute a color picture if possible. */
  242.     if (hasColorQD) {
  243.         GetDItem(win, diSplashPicture, &itemtype, &itemhandle, &itemrect);
  244.         if ((pic = (PicHandle) GetResource('PICT', pSplashColor)) != nil) {
  245.             SetDItem(win, diSplashPicture, itemtype, (Handle) pic, &itemrect);
  246.         }
  247.     }
  248.     /* (should enable/disable connect button depending on networking ability) */
  249.     if (can_connect()) {
  250.     } else {
  251.     }
  252.     ShowWindow(win);
  253.     SelectWindow(win);
  254.     ModalDialog((ModalFilterProcPtr) filter_splash, &ditem);
  255.     /* We don't loop around here, just return the ditem and let caller decide what to do. */
  256.     DisposDialog(win);
  257.     return ditem;
  258. }
  259.  
  260. /* Dialog app-defined item callback that displays the list of possible games. */
  261.  
  262. pascal void
  263. draw_game_list(win, ditem)
  264. WindowPtr win;
  265. short ditem;
  266. {
  267.     short itemtype;  Handle itemhandle;  Rect itemrect;
  268.  
  269.     GetDItem(newgamewin, diNewGameList, &itemtype, &itemhandle, &itemrect);
  270.     /* Draw the list of available games. */
  271.     LUpdate(newgamewin->visRgn, newgamelist);
  272.     /* Frame it nicely. */
  273.     --itemrect.left;
  274.     FrameRect(&itemrect);
  275. }
  276.  
  277. /* Dialog app-defined item callback to display the game description. */
  278.  
  279. pascal void
  280. draw_game_blurb(win, ditem)
  281. WindowPtr win;
  282. short ditem;
  283. {
  284.     GrafPtr oldport;
  285.     short itemtype;  Handle itemhandle;  Rect itemrect;
  286.  
  287.     GetDItem(newgamewin, diNewGameBlurb, &itemtype, &itemhandle, &itemrect);
  288.     /* Make sure the text is up-to-date. */
  289.     GetPort(&oldport);
  290.     SetPort(newgamewin);
  291.     TEUpdate(&itemrect, newgametext);    
  292.     SetPort(oldport);
  293.     /* Frame it. */
  294.     InsetRect(&itemrect, -1, -1);
  295.     FrameRect(&itemrect);
  296. }
  297.  
  298. /* Dialog app-defined item callback to display the game picture, if available. */
  299.  
  300. pascal void
  301. draw_game_picture(win, ditem)
  302. WindowPtr win;
  303. short ditem;
  304. {
  305.     RgnHandle tmprgn;
  306.     Rect cliprect;
  307.     short itemtype;  Handle itemhandle;  Rect itemrect;
  308.  
  309.     GetDItem(newgamewin, diNewGamePicture, &itemtype, &itemhandle, &itemrect);
  310.     EraseRect(&itemrect);
  311.     if (newgamepicture != nil) {
  312.         tmprgn = NewRgn();
  313.         GetClip(tmprgn);
  314.         cliprect = itemrect;
  315.         ClipRect(&cliprect);
  316.         center_pict(newgamepicture, &itemrect);
  317.         DrawPicture(newgamepicture, &itemrect);
  318.         SetClip(tmprgn);
  319.         DisposeRgn(tmprgn);
  320.     }
  321. #ifdef DESIGNERS
  322.     /* A low-impact feedback that this game will launch directly into designing. */
  323.     if (allbedesigners) {
  324.         PenSize(3, 3);
  325.         itemrect.right -= 3;  itemrect.bottom -= 3;
  326.         FrameRect(&itemrect);
  327.         PenNormal();
  328.     }
  329. #endif /* DESIGNERS */
  330. }
  331.  
  332. /* Utility that figures out how to center a picture in a rectangle. */
  333.  
  334. center_pict(picture, rect)
  335. PicHandle picture;
  336. Rect *rect;
  337. {
  338.     int picth, pictv;
  339.     Rect initrect, pictrect;
  340.  
  341.     initrect = *rect;
  342.     pictrect = (*picture)->picFrame;
  343.     picth = pictrect.right - pictrect.left;  pictv = pictrect.bottom - pictrect.top;
  344.     rect->left += (initrect.right - initrect.left) / 2 - picth / 2;
  345.     rect->top += (initrect.bottom - initrect.top) / 2 - pictv / 2;
  346.     rect->right = rect->left + picth;
  347.     rect->bottom = rect->top + pictv;
  348. }
  349.  
  350. Module *
  351. module_from_cell(cell)
  352. Cell cell;
  353. {
  354.     return (cell.v < numgames ? possiblegames[cell.v] : NULL);
  355. }
  356.  
  357. /* This filter for the new game dialog just handles the game list, updating the
  358.    other items to reflect the currently selected game. */
  359.  
  360. int lastnewgameclick;
  361. Point lastnewgamemouse;
  362.  
  363. pascal Boolean
  364. filter_new_game(dialog, evt, itemhit)
  365. DialogPtr dialog;
  366. EventRecord *evt;
  367. short *itemhit;
  368. {
  369.     GrafPtr oldport;
  370.     Point pt, origpt;
  371.     short ditem;
  372.     char ch;
  373.  
  374.     /* Look for the right kind of event. */
  375.     switch (evt->what) {
  376.         case mouseDown:
  377.             GetPort(&oldport);
  378.             SetPort(newgamewin);
  379.             pt = origpt = evt->where;
  380.             GlobalToLocal(&pt);
  381.             ditem = FindDItem(newgamewin, pt) + 1;
  382.             if (ditem == diNewGameList) {
  383.                 /* Loop around in list handling. */
  384.                 LClick(pt, evt->modifiers, newgamelist);
  385.                 /* We're finished clicking, show the results. */
  386.                 select_game();
  387.                 display_selected_game();
  388.                 SetPort(oldport);
  389.                 if (TickCount() - lastnewgameclick < GetDblTime()
  390.                     && between(-3, origpt.h - lastnewgamemouse.h, 3)
  391.                     && between(-3, origpt.v - lastnewgamemouse.v, 3)
  392.                     ) {
  393.                     *itemhit = diNewGameOK;
  394.                 } else {
  395.                     lastnewgameclick = TickCount();
  396.                     lastnewgamemouse = evt->where;
  397.                     *itemhit = diNewGameList;
  398.                 }
  399.                 return TRUE;
  400.             } else {
  401.                 SetPort(oldport);
  402.                 return FALSE;
  403.             }
  404.             break;
  405.         case keyDown:
  406.             ch = evt->message & charCodeMask;
  407.             if (ch == 3 || ch == 13) {
  408.                 if (selectedgame) {
  409.                     *itemhit = diNewGameOK;
  410.                     return TRUE;
  411.                 }
  412.             }
  413. #ifdef DESIGNERS
  414.             /* A secret way to get into designing at startup. */
  415.             if (ch == 'd') {
  416.                 allbedesigners = !allbedesigners;
  417.                 DrawDialog(newgamewin);
  418.             }
  419. #endif
  420. #ifdef DEBUGGING
  421.             /* A secret way to get debugging at startup. */
  422.             if (ch == 'D') {
  423.                 toggle_debugging(&Debug);
  424.                 DrawDialog(newgamewin);
  425.             }
  426.             if (ch == 'M') {
  427.                 toggle_debugging(&DebugM);
  428.                 DrawDialog(newgamewin);
  429.             }
  430.             if (ch == 'G') {
  431.                 toggle_debugging(&DebugG);
  432.                 DrawDialog(newgamewin);
  433.             }
  434. #endif
  435.             break;
  436.     }
  437.     return FALSE;
  438. }
  439.  
  440. /* Given a selected game, display assorted info about it - basically a preview so that
  441.    prospective players can see what they're getting into.  If no game has been selected,
  442.    then this routine clears the displays. */
  443.  
  444. display_selected_game()
  445. {
  446.     short itemtype;  Handle itemhandle;  Rect itemrect;
  447.     char *desc, buf[BUFSIZE];
  448.     Str255 tmpstr;
  449.     PicHandle oldnewgamepicture;
  450.  
  451.     DGprintf("display selected game %s\n", module_desig(selectedgame));
  452.     /* Set the blurb for the selected game. */
  453.     GetDItem(newgamewin, diNewGameBlurb, &itemtype, &itemhandle, &itemrect);
  454.     TESetSelect(0, 100000, newgametext);
  455.     TECut(newgametext);
  456.     if (selectedgame /* && selectedgame->complete */) {
  457.         if (selectedgame->blurb) {
  458.             desc = selectedgame->blurb;
  459.         } else {
  460.             desc = "??? experimental ???";
  461.         }
  462.     } else {
  463.         desc = "";
  464.     }
  465.     TESetText(desc, strlen(desc), newgametext);
  466.     TEUpdate(&itemrect, newgametext);
  467.     /* Load a picture if one can be found. */
  468.     /* (should look for picture explicitly named in a game-module slot first) */
  469.     oldnewgamepicture = newgamepicture;
  470.     newgamepicture = nil;
  471.     if (selectedgame) {
  472.         sprintf(spbuf, "%s game", selectedgame->name);
  473.         c2p(spbuf, tmpstr);
  474.         newgamepicture = (PicHandle) GetNamedResource('PICT', tmpstr);
  475.         if (newgamepicture == nil) {
  476.             sprintf(spbuf, "%s", selectedgame->name);
  477.             c2p(spbuf, tmpstr);
  478.             newgamepicture = (PicHandle) GetNamedResource('PICT', tmpstr);
  479.         }
  480.     } else {
  481.         /* Nothing to display - how boring! */
  482.     }
  483.     /* Gray out the OK button if no game selected. */
  484.     GetDItem(newgamewin, diNewGameOK, &itemtype, &itemhandle, &itemrect);
  485.     HiliteControl((ControlHandle) itemhandle, (selectedgame ? 0 : 255));
  486.     /* We have to force redraw to get the picture item updated. */
  487.     if (oldnewgamepicture != newgamepicture) DrawDialog(newgamewin);
  488. }
  489.  
  490. /* This is a modal dialog from which the user selects a game and sets options. */
  491.  
  492. new_game_dialog()
  493. {
  494.     int done = FALSE;
  495.     short ditem, i;
  496.     Point pnt, cellsize;
  497.     Cell tmpcell;
  498.     Rect listrect, destrect, viewrect;
  499.     Module *module;
  500.     char *gamename;
  501.     short itemtype;  Handle itemhandle;  Rect itemrect;
  502.  
  503.     collect_possible_games();
  504.     if (newgamewin == nil) {
  505.         newgamewin = GetNewDialog(dNewGame, NULL, (DialogPtr) -1);
  506.         SetPort(newgamewin);
  507.         TextFont(newYork);
  508. /*        TextSize(10); */
  509.         /* Set up the app-defined item that lists games. */
  510.         GetDItem(newgamewin, diNewGameList, &itemtype, &itemhandle, &itemrect);
  511.         SetDItem(newgamewin, diNewGameList, itemtype, (Handle) draw_game_list, &itemrect);
  512.         SetPt(&cellsize, 0, 0);
  513.         listrect.top = 0;  listrect.left = 0;
  514.         listrect.bottom = 0;  listrect.right = 1;
  515.         itemrect.top += 1;
  516.         itemrect.bottom -= 1;  itemrect.right -= sbarwid + 1;
  517.         /* Create the list of games itself and fill it in. */
  518.         newgamelist = LNew(&itemrect, &listrect, cellsize, 0, newgamewin, 0,0,0,TRUE);
  519.         SetPt(&tmpcell, 0, 0);
  520.         for (i = 0; i < numgames; ++i) {
  521.             module = possiblegames[i];
  522.             gamename = module->title;
  523.             if (gamename == NULL) gamename = module->name;
  524.             if (gamename == NULL) gamename = "???";
  525.             LAddRow(1, tmpcell.v, newgamelist);
  526.             LSetCell(gamename, strlen(gamename), tmpcell, newgamelist);
  527.             ++tmpcell.v;
  528.         }
  529.         GetDItem(newgamewin, diNewGameBlurb, &itemtype, &itemhandle, &itemrect);
  530.         SetDItem(newgamewin, diNewGameBlurb, itemtype, (Handle) draw_game_blurb, &itemrect);
  531.         destrect = itemrect;
  532.         viewrect = itemrect;
  533. /*        TextSize(10);  */
  534.         newgametext = TENew(&destrect, &viewrect);
  535.         GetDItem(newgamewin, diNewGamePicture, &itemtype, &itemhandle, &itemrect);
  536.         SetDItem(newgamewin, diNewGamePicture, itemtype, (Handle) draw_game_picture, &itemrect);
  537.         newgamepicture = nil;
  538.     }
  539.     update_new_game_list();
  540.     SetPt(&tmpcell, 0, 0);
  541.     LSetSelect(TRUE, tmpcell, newgamelist);  /* (do this only for new/changed list?) */
  542.     select_game();
  543.     display_selected_game();
  544.     SetCursor(&QD(arrow));
  545.     ShowWindow(newgamewin);
  546.     LDoDraw(1, newgamelist);
  547.     /* Loop around here until the player picks a game to play, or cancels. */
  548.     while (!done) {
  549.         draw_default_button(newgamewin, diNewGameOK);
  550.         ModalDialog((ModalFilterProcPtr) filter_new_game, &ditem);
  551.         switch (ditem) {
  552.             case diNewGameOK:
  553.                 /* Make this dialog disappear now, startup may take a long time. */
  554.                 HideWindow(newgamewin);
  555.                 if (start_new_game()) {
  556.                     done = TRUE;
  557.                 } else {
  558.                     /* If we failed to start the new game, just go around again. */
  559.                     ShowWindow(newgamewin);
  560.                     SelectWindow(newgamewin);
  561.                 }
  562.                 break;
  563.             case diNewGameCancel:
  564.                 done = TRUE;
  565.                 break;
  566.             default:
  567.                 break;
  568.         }
  569.     }
  570.     /* We're done, OK to throw away the dialog. */
  571.     DisposDialog(newgamewin);
  572.     newgamewin = nil;
  573.     /* (should release TEs and lists also) */
  574. }
  575.  
  576. /* The comparison function for the game list puts un-formally-named modules at the end,
  577.    plus the default sorting puts initial-lowercased names after uppercased ones. */
  578.  
  579. module_name_compare(mp1, mp2)
  580. Module **mp1, **mp2;
  581. {
  582.     if ((*mp1)->title == NULL) {
  583.         if ((*mp2)->title == NULL) {
  584.             /* (this assumes we have no entirely unnamed modules) */
  585.             return strcmp((*mp1)->name, (*mp2)->name);
  586.         } else {
  587.             return 1;
  588.         }
  589.     } else {
  590.         if ((*mp2)->title == NULL) {
  591.             return (-1);
  592.         } else {
  593.             return strcmp((*mp1)->title, (*mp2)->title);
  594.         }
  595.     }
  596. }
  597.  
  598. /* This routine finds and lists all the games that should be listed as choices for
  599.    the user. */
  600.  
  601. collect_possible_games()
  602. {
  603.     int numresources, i;
  604.     Handle modulehandle;
  605.     short moduleid;
  606.     char *modulename = NULL, *modulecontents = NULL;
  607.     ResType restype;
  608.     Str255 resname;
  609.     Module *module;
  610.  
  611.     if (numgames == 0 && !game_already_loaded()) {
  612.         numresources = CountResources('XCgm');
  613.         /* Make enough room to record all the possible games. */
  614.         possiblegames = (Module **) xmalloc((numresources + 1) * sizeof(Module *));
  615.         /* Collect the intro game module and put at head of list. */
  616.         module = get_game_module(firstgamename);
  617.         add_to_possible_games(module);
  618.         /* Pick up game modules that are wired in as resources. */
  619.         for (i = 0; i < numresources; ++i) {
  620.             modulehandle = GetIndResource('XCgm', i + 1);
  621.             /* (should test for resource validity?) */
  622.             if (0 /* size > 0 */) {
  623.                 /* set modulecontents from resource */
  624.                 modulecontents = NULL;
  625.             }
  626.             /* Try to pick up module name from its resource name, otherwise
  627.                assume its name in its content. */
  628.             GetResInfo(modulehandle, &moduleid, &restype, &resname);
  629.             if (resname[0] > 0) {
  630.                 resname[resname[0]+1] = '\0';
  631.                 modulename = copy_string(resname+1);
  632.             } else {
  633.                 modulename = NULL;
  634.             }
  635.             module = get_game_module(modulename);
  636.             module->contents = modulecontents;
  637.             add_to_possible_games(module);
  638.         }
  639. #if 0
  640.         /* (eventually add random library files) */
  641.         /* (will have to count them first) */
  642.         printf("Lib is %s\n", xconqlib);
  643.         sprintf(spbuf, "%s:", xconqlib);
  644.         pascalify(spbuf);
  645.         for (i = 0; i < 10; ++i) {
  646.             FInfo info;
  647.             OSErr rslt = GetFInfo(tmpbuf, i, &info);
  648.             
  649.             printf("%d: rslt is %d\n", i, rslt);
  650.             if (rslt == noErr) {
  651.                 printf("  %c %c %d %d %d\n",
  652.                         info.fdType, info.fdCreator, info.fdFlags,
  653.                         info.fdLocation, info.fdFldr);
  654.             }
  655.         }
  656. #endif
  657.         if (numgames > 2) {
  658.             /* Sort all but the first game into alphabetical order by displayed name. */
  659.             qsort(&(possiblegames[1]), numgames - 1, sizeof(Module *), module_name_compare);
  660.         }
  661.     }
  662. }
  663.  
  664. /* Load a game's description and add it to the list of games. */
  665.  
  666. add_to_possible_games(module)
  667. Module *module;
  668. {
  669.     int i;
  670.  
  671.     if (module != NULL) {
  672.         if (load_game_description(module)) {
  673.             /* It might be that the module description supplies the real name,
  674.                and that the module already exists. (work on this) */
  675.               /* Don't add duplicate modules. */
  676.             for (i = 0; i < numgames; ++i) {
  677.                 if (possiblegames[i] == module) return;
  678.             }
  679.               if (numgames < 100) {
  680.                 possiblegames[numgames++] = module;
  681.             }
  682.         }
  683.     }
  684. }
  685.  
  686. /* Select a game from the list of possible games. */
  687.  
  688. select_game()
  689. {
  690.     Cell tmpcell;
  691.  
  692.     SetPt(&tmpcell, 0, 0);
  693.     if (game_already_loaded()) {
  694.         /* selectedgame is already set correctly. */
  695.     } else if (LGetSelect(TRUE, &tmpcell, newgamelist)) {
  696.         selectedgame = module_from_cell(tmpcell);
  697.     } else {
  698.         selectedgame = NULL;
  699.     }
  700. }
  701.  
  702. /* When the new game OK button is hit, this will do the work of getting things started,
  703.    possibly pausing to ask about player/side setup. */
  704.  
  705. start_new_game()
  706. {
  707.     if (selectedgame == NULL) { /* Just in case... */
  708.         SysBeep(20);
  709.         return FALSE;
  710.     }
  711.     if (!game_already_loaded()) {
  712.         /* Suck in the selected module.  This cannot be undone (yet). */
  713.         mainmodule = selectedgame;
  714.         load_game_module(selectedgame);
  715.         /* Change cursor back, in case it was different during loading. */
  716.         SetCursor(&QD(arrow));
  717.         /* If the loaded game is not valid, we will get an alert somewhere in here,
  718.            and possibly bomb out if the player chooses not to continue. */
  719.         check_game_validity();
  720.     }
  721.     return launch_game();
  722. }
  723.  
  724. /* This routine is for when we end up back in the new game dialog,
  725.    after something has already been loaded. */
  726.  
  727. /* (could also include any game that has this one as a base module) */
  728.  
  729. update_new_game_list()
  730. {
  731.     char *gamename;
  732.     Point tmpcell;
  733.     
  734.     if (newgamelist != nil && game_already_loaded()) {
  735.         SetPt(&tmpcell, 0, 0);
  736.         /* Clear out the entire list. */
  737.         LDelRow(0, 0, newgamelist);
  738.         /* Add the loaded game back. */ 
  739.         LAddRow(1, tmpcell.v, newgamelist);
  740.         /* Find a name to paste into the list. */
  741.         gamename = selectedgame->title;
  742.         if (gamename == NULL) gamename = selectedgame->name;
  743.         if (gamename == NULL) gamename = "???";
  744.         /* Set and select the one cell. */
  745.         LSetCell(gamename, strlen(gamename), tmpcell, newgamelist);
  746.         LSetSelect(TRUE, tmpcell, newgamelist);
  747.     }
  748. }
  749.  
  750. pascal void
  751. draw_variant_slider(win, ditem)
  752. WindowPtr win;
  753. short ditem;
  754. {
  755.     Str255 tmpstr;
  756.     short itemtype;  Handle itemhandle;  Rect itemrect;
  757.  
  758.     GetDItem(win, ditem, &itemtype, &itemhandle, &itemrect);
  759.     /* Always frame. */
  760.     FrameRect(&itemrect);
  761.     if (between(diVariantsFirstSlider, ditem, diVariantsFirstSlider + numsliders - 1)) {
  762.         MoveTo(itemrect.left + 2, itemrect.top + 10);
  763.         c2p(slidernames[ditem - diVariantsFirstSlider], tmpstr);
  764.         DrawString(tmpstr);
  765.     } else {
  766.         gray_out_rect(&itemrect);
  767.     }
  768. }
  769.  
  770. pascal void
  771. draw_variant_help(win, ditem)
  772. WindowPtr win;
  773. short ditem;
  774. {
  775.     short itemtype;  Handle itemhandle;  Rect itemrect;
  776.  
  777.     GetDItem(win, ditem, &itemtype, &itemhandle, &itemrect);
  778.     /* Always frame. */
  779.     FrameRect(&itemrect);
  780. }
  781.  
  782. /* The variants dialog basically handles all the player-settable options defined
  783.    by a game. */
  784.  
  785. variants_dialog()
  786. {
  787.     int i, done = FALSE, changed = FALSE;
  788.     char *gamename;
  789.     Str255 tmpstr;
  790.     short ditem;
  791.     short itemtype;  Handle itemhandle;  Rect itemrect;
  792.  
  793.     interpret_variants();
  794.     if (!anyvariants) return TRUE;
  795.     if (variantswin == nil) {
  796.         variantswin = GetNewDialog(dVariants, NULL, (DialogPtr) -1L);
  797.         /* Set the title of this dialog appropriately. */
  798.         if (selectedgame != NULL) {
  799.             gamename = selectedgame->title;
  800.             if (gamename == NULL) gamename = selectedgame->name;
  801.             if (gamename == NULL) gamename = "???";
  802.         } else {
  803.             gamename = "?";
  804.         }
  805.         sprintf(spbuf, "Variants for \"%s\"", gamename);
  806.         c2p(spbuf, tmpstr);
  807.         GetDItem(variantswin, diVariantsText, &itemtype, &itemhandle, &itemrect);
  808.         SetIText(itemhandle, tmpstr);
  809.         /* Give all the slider app items the same proc. */
  810.         for (i = diVariantsFirstSlider; i < (diVariantsFirstSlider + MAXSLIDERS); ++i) {
  811.             GetDItem(variantswin, i, &itemtype, &itemhandle, &itemrect);
  812.             SetDItem(variantswin, i, itemtype, (Handle) draw_variant_slider, &itemrect);
  813.         }
  814.         GetDItem(variantswin, diVariantsHelp, &itemtype, &itemhandle, &itemrect);
  815.         SetDItem(variantswin, diVariantsHelp, itemtype, (Handle) draw_variant_help, &itemrect);
  816.     }
  817.     /* Display/default the standard variants appropriately. */
  818.     set_variant_item(diVariantsWorldSeen, varyseen, TRUE, newseen);
  819.     set_variant_item(diVariantsSeeAll, varyallseen, TRUE, newseeall);
  820.     set_variant_item(diVariantsWorldSize, varyworld, FALSE, 0);
  821.     /* For each random checkbox used, give it a title and make it displayable,
  822.        otherwise gray it. */
  823.     for (i = 0; i < MAXCHECKBOXES; ++i) {
  824.         GetDItem(variantswin, diVariantsFirstCheckBox + i, &itemtype, &itemhandle, &itemrect);
  825.         if (i < numcheckboxes) {
  826.             c2p(checkboxnames[i], tmpstr);
  827.             SetCTitle((ControlHandle) itemhandle, tmpstr);
  828.             ShowControl((ControlHandle) itemhandle);
  829.             /* should set its initial state based on variant's default */
  830.         } else {
  831.             sprintf(spbuf, "Unused", i);
  832.             c2p(spbuf, tmpstr);
  833.             SetCTitle((ControlHandle) itemhandle, tmpstr);
  834.             ShowControl((ControlHandle) itemhandle);
  835.             HiliteControl((ControlHandle) itemhandle, 255);
  836.         }
  837.     }
  838.     ShowWindow(variantswin);
  839.     while (!done) {
  840.         draw_default_button(variantswin, diVariantsOK);
  841.         ModalDialog(NULL, &ditem);
  842.         switch (ditem) {
  843.             case diVariantsOK:
  844.                 implement_variants();
  845.                 changed = TRUE;
  846.                 /* Fall through to next case. */
  847.             case diVariantsCancel:
  848.                 done = TRUE;
  849.                 break;
  850.             case diVariantsWorldSeen:
  851.             case diVariantsSeeAll:
  852.                 /* Toggle check boxes. */
  853.                 GetDItem(variantswin, ditem, &itemtype, &itemhandle, &itemrect);
  854.                 toggle_checkbox(itemhandle);
  855.                 break;
  856.             case diVariantsWorldSize:
  857.                 /* Fire up a separate dialog for world geometry. */
  858.                 world_shape_dialog();
  859.                 break;
  860.             default:
  861.                 /* Handle all the checkboxes similarly. */
  862.                 if (between(diVariantsFirstCheckBox, ditem, diVariantsFirstCheckBox+MAXCHECKBOXES-1)) {
  863.                     GetDItem(variantswin, ditem, &itemtype, &itemhandle, &itemrect);
  864.                     toggle_checkbox(itemhandle);
  865.                 }
  866.                 break;
  867.         }
  868.     }
  869.     DisposDialog(variantswin);
  870.     variantswin = nil;
  871.     return changed;
  872. }
  873.  
  874. set_variant_item(di, flag, flag2, val)
  875. int di;
  876. int flag, flag2, val;
  877. {
  878.     short itemtype;  Handle itemhandle;  Rect itemrect;
  879.  
  880.     GetDItem(variantswin, di, &itemtype, &itemhandle, &itemrect);
  881.     ShowControl((ControlHandle) itemhandle);
  882.     HiliteControl((ControlHandle) itemhandle, (flag ? 0 : 255));
  883.     if (flag && flag2) {
  884.         SetCtlValue((ControlHandle) itemhandle, val);
  885.     }
  886. }
  887.  
  888. /* Go through all the variants and set up appropriate flags. */
  889.  
  890. interpret_variants()
  891. {
  892.     Obj *vars, *var, *vartype, *vardata, *vartmp;
  893.     char *varname, *vartypename;
  894.  
  895.     varyseen = varyallseen = varyworld = anyvariants = FALSE;
  896.     if (!selectedgame) return;
  897.     numcheckboxes = 0;
  898.     numsliders = 0;
  899.     for (vars = selectedgame->variants; vars != lispnil; vars = cdr(vars)) {
  900.         anyvariants = TRUE;
  901.         var = car(vars);
  902.         if (symbolp(var)) {
  903.             var = cons(var, lispnil);
  904.         } else if (!consp(var)) {
  905.             /* already warned about */
  906.             continue;
  907.         }
  908.         varname = c_string(car(var));
  909.         if (stringp(car(var))) {
  910.             var = cdr(var);
  911.         }
  912.         vartype = car(var);
  913.         vardata = cdr(var);
  914.         vartypename = c_string(vartype);
  915.         switch (keyword_code(vartypename)) {
  916.             case K_WORLD_SEEN:
  917.                 varyseen = TRUE;
  918.                 newseen = FALSE;
  919.                 if (vardata != lispnil) {
  920.                     vartmp = eval(car(vardata));
  921.                     if (numberp(vartmp)) {
  922.                         newseen = c_number(vartmp);
  923.                     }
  924.                 }
  925.                 break;
  926.             case K_SEE_ALL:
  927.                 varyallseen = TRUE;
  928.                 newseeall = FALSE;
  929.                 if (vardata != lispnil) {
  930.                     vartmp = eval(car(vardata));
  931.                     if (numberp(vartmp)) {
  932.                         newseeall = c_number(vartmp);
  933.                     }
  934.                 }
  935.                 break;
  936.             case K_WORLD_SIZE:
  937.                 varyworld = TRUE;
  938.                 /* Start with some defaults. */
  939.                 newcircumference = DEFAULTCIRCUMFERENCE;
  940.                 newwidth = DEFAULTWIDTH;  newheight = DEFAULTHEIGHT;
  941.                 newlatitude = 0;  newlongitude = 0;
  942.                 break;
  943.             default:
  944.                 if (1 /* boolean variant */) {
  945.                     if (numcheckboxes >= MAXCHECKBOXES) {
  946.                         init_warning("too many variants, can't set all of them");
  947.                         break;
  948.                     }
  949.                     checkboxnames[numcheckboxes] = varname;
  950.                     checkboxes[numcheckboxes] = var;
  951.                     ++numcheckboxes;
  952.                 } else {
  953.                     /* (should set up slider) */
  954.                 }
  955.                 break;
  956.         }
  957.     }
  958. }
  959.  
  960. /* This is where we actually change the state of the game according to the variants. */
  961.  
  962. /* (should share more with other interfaces) */
  963.  
  964. implement_variants()
  965. {
  966.     int i;
  967.     short itemtype;  Handle itemhandle;  Rect itemrect;
  968.  
  969.     variants = lispnil;
  970.     if (varyworld) {
  971.         /* It is critically important that users not be able to reshape already-alloced
  972.            areas, but do let them know that their request had to be overridden. */
  973.         if (((area.width > 0 && area.width != newwidth)
  974.             || (area.height > 0 && area.height != newheight)
  975.             || (world.circumference > 0 && world.circumference != newcircumference))
  976.             && (1 /* some layers (probably) allocated already */)) {
  977.             /* (this is misleading, is an "expected" alert) */
  978.             init_warning("Area dimensions must remain %d x %d, %d around world",
  979.                          area.width, area.height, world.circumference);
  980.             newwidth = area.width;  newheight = area.height;
  981.             newcircumference = world.circumference;
  982.         }
  983.         variants = cons(cons(intern_symbol("world-size"),
  984.                              cons(new_number(newwidth),
  985.                                    cons(new_number(newheight),
  986.                                        cons(new_number(newcircumference),
  987.                                             cons(new_number(newlongitude),
  988.                                                  cons(new_number(newlatitude),
  989.                                               lispnil)))))),
  990.                         variants);
  991.     }
  992.     if (varyseen) {
  993.         GetDItem(variantswin, diVariantsWorldSeen, &itemtype, &itemhandle, &itemrect);
  994.         variants = cons(cons(intern_symbol("world-seen"),
  995.                              cons(intern_symbol(GetCtlValue((ControlHandle) itemhandle) ?
  996.                                                  "true" : "false"),
  997.                                   lispnil)),
  998.                         variants);
  999.     }
  1000.     if (varyallseen) {
  1001.         GetDItem(variantswin, diVariantsSeeAll, &itemtype, &itemhandle, &itemrect);
  1002.         variants = cons(cons(intern_symbol("see-all"),
  1003.                              cons(intern_symbol(GetCtlValue((ControlHandle) itemhandle) ?
  1004.                                                  "true" : "false"),
  1005.                                   lispnil)),
  1006.                         variants);
  1007.     }
  1008.     /* Implement the random checkbox variants. */
  1009.     for (i = 0; i < numcheckboxes; ++i) {
  1010.         GetDItem(variantswin, diVariantsFirstCheckBox + i, &itemtype, &itemhandle, &itemrect);
  1011.         if (GetCtlValue((ControlHandle) itemhandle)) {
  1012.             variants = cons(cons(car(checkboxes[i]),
  1013.                                  cons(intern_symbol("true"),
  1014.                                       lispnil)),
  1015.                             variants);
  1016.         }
  1017.     }
  1018.     /* (should implement the random slider variants) */
  1019.     do_module_variants(selectedgame, variants);
  1020.     /* Recheck everything, the variants might have broken something. */
  1021.     if (anyvariants) {
  1022.         check_game_validity();
  1023.     }
  1024. }
  1025.  
  1026. void
  1027. special_xform(x, y, sxp, syp)
  1028. int x, y, *sxp, *syp;
  1029. {
  1030.     int hw = 1, hh = 1, hch = 1, sx = newheight / 4, sy = 0, totsh = newheight;
  1031.  
  1032.     *sxp = x * hw + (y * hw) / 2 - sx;
  1033.     *syp = totsh - y * hch - sy;
  1034. }
  1035.  
  1036.  
  1037. /* This is a callback that draws the world and its areas. */
  1038.  
  1039. /* (Would be really cool to draw accurate curvature over globe here) */
  1040.  
  1041. pascal void
  1042. draw_world_picture(win, ditem)
  1043. WindowPtr win;
  1044. short ditem;
  1045. {
  1046.     int llx, lly, lrx, lry, rx, ry, urx, ury, ulx, uly, lx, ly;
  1047.     Point dims, center;
  1048.     Rect worldrect, arearect;
  1049.     PolyHandle poly;
  1050.     short itemtype;  Handle itemhandle;  Rect itemrect;
  1051.  
  1052.     GetDItem(worldshapewin, diWorldShapePicture, &itemtype, &itemhandle, &itemrect);
  1053.     /* Make a framed gray background for the world picture. */
  1054.     FillRect(&itemrect, QD(gray));
  1055.     FrameRect(&itemrect);
  1056.     InsetRect(&itemrect, 10, 10);
  1057.     dims.h = itemrect.right - itemrect.left;  dims.v = itemrect.bottom - itemrect.top;
  1058.     center.h = itemrect.left + dims.h / 2;  center.v = itemrect.top + dims.v / 2;
  1059.     worldrect = itemrect;
  1060.     /* Draw the whole world as a circle if it's not too big. */
  1061.     if (newcircumference < newwidth) {
  1062.         /* If world circumference smaller than area, world is not being used at all. */
  1063.     } else if (newcircumference / 2 <= dims.h) {
  1064.         InsetRect(&worldrect, (dims.h - newcircumference / 2) / 2, (dims.v - newcircumference / 2) / 2);
  1065.         EraseOval(&worldrect);
  1066.         FrameOval(&worldrect);
  1067.     } else {
  1068.         EraseRect(&worldrect);
  1069.     }
  1070.     /* Draw some grid lines. */
  1071.     MoveTo(worldrect.left, center.v);
  1072.     LineTo(worldrect.right, center.v);
  1073.     /* (should draw 30 and 60 lat also, if globe displayed) */
  1074.     MoveTo(center.h, worldrect.top);
  1075.     LineTo(center.h, worldrect.bottom);
  1076.     if (newwidth == newcircumference) {
  1077.         /* should show better globe-girdling area */
  1078.         arearect.top = center.v - newheight / 2;
  1079.         arearect.left = center.h - newwidth / 2;
  1080.         arearect.bottom = center.v + newheight / 2;
  1081.         arearect.right = center.h + newwidth / 2;
  1082.         OffsetRect(&arearect, newlatitude, newlongitude);
  1083.         FillRect(&arearect, QD(dkGray));
  1084.     } else if (newwidth < newcircumference / 2 && newheight < newcircumference / 2) {
  1085.         if (1 /* area is a hexagon */) {
  1086.             poly = OpenPoly();        
  1087.             special_xform(0 + newheight/2, 0, &llx, &lly);
  1088.             MoveTo(llx, lly);
  1089.             special_xform(newwidth-1, 0, &lrx, &lry);
  1090.              LineTo(lrx, lry);
  1091.             special_xform(newwidth-1, newheight/2, &rx, &ry);
  1092.             LineTo(rx, ry);
  1093.              special_xform(newwidth-1 - newheight/2, newheight-1, &urx, &ury);
  1094.             LineTo(urx, ury);
  1095.              special_xform(0, newheight-1, &ulx, &uly);
  1096.             LineTo(ulx, uly);
  1097.              special_xform(0, newheight/2, &lx, &ly);
  1098.             LineTo(lx, ly);
  1099.             LineTo(llx, lly);
  1100.             ClosePoly();
  1101.             OffsetPoly(poly, center.h - newwidth / 2, center.v - newheight / 2);
  1102.             OffsetPoly(poly, newlatitude, newlongitude);
  1103.             FillPoly(poly, QD(dkGray));
  1104.         } else {
  1105.             arearect.top = center.v - newheight / 2;
  1106.             arearect.left = center.h - newwidth / 2;
  1107.             arearect.bottom = center.v + newheight / 2;
  1108.             arearect.right = center.h + newwidth / 2;
  1109.             OffsetRect(&arearect, newlatitude, newlongitude);
  1110.             FillRect(&arearect, QD(dkGray));
  1111.         }
  1112.     } else {
  1113.         /* (should show a partially wrapped area) */
  1114.     }
  1115. }
  1116.  
  1117. #define put_number_into_ditem(di,num)  \
  1118.   GetDItem(worldshapewin, (di), &itemtype, &itemhandle, &itemrect);  \
  1119.   NumToString((num), tmpstr);  \
  1120.   SetIText(itemhandle, tmpstr);
  1121.  
  1122. #define get_number_from_ditem(di,place)  \
  1123.   GetDItem(worldshapewin, (di), &itemtype, &itemhandle, &itemrect);  \
  1124.   GetIText(itemhandle, tmpstr);  \
  1125.   StringToNum(tmpstr, &(place));
  1126.  
  1127. pascal Boolean
  1128. filter_world_setup(dialog, evt, itemhit)
  1129. DialogPtr dialog;
  1130. EventRecord *evt;
  1131. short *itemhit;
  1132. {
  1133.     GrafPtr oldport;
  1134.     Point pt, dims, center;
  1135.     short ditem;
  1136.     int wid, hgt;
  1137.     char ch;
  1138.     Str255 tmpstr;
  1139.     short itemtype;  Handle itemhandle;  Rect itemrect;
  1140.  
  1141.     switch (evt->what) {
  1142.         case mouseDown:
  1143.             GetPort(&oldport);
  1144.             SetPort(worldshapewin);
  1145.             pt = evt->where;
  1146.             GlobalToLocal(&pt);
  1147.             ditem = FindDItem(worldshapewin, pt) + 1;
  1148.             if (ditem == diWorldShapePicture) {
  1149.                 GetDItem(worldshapewin, diWorldShapePicture, &itemtype, &itemhandle, &itemrect);
  1150.                 dims.h = itemrect.right - itemrect.left;  dims.v = itemrect.bottom - itemrect.top;
  1151.                 center.h = itemrect.left + dims.h / 2;  center.v = itemrect.top + dims.v / 2;
  1152.                 wid = 2 * abs(pt.h - center.h);  hgt = 2 * abs(pt.v - center.v);
  1153.                 /* (should adjust the cursor to indicate valid places) */
  1154.                 if (valid_area_shape(wid, hgt, FALSE)) {
  1155.                     put_number_into_ditem(diWorldShapeWidth, wid);
  1156.                     put_number_into_ditem(diWorldShapeHeight, hgt);
  1157.                     /* The outer dialog loop will do the redraw. */
  1158.                 }
  1159.                 SetPort(oldport);
  1160.                 *itemhit = diWorldShapePicture;
  1161.                 return TRUE;
  1162.             } else {
  1163.                 SetPort(oldport);
  1164.             }
  1165.             break;
  1166.         case keyDown:
  1167.             ch = evt->message & charCodeMask;
  1168.             if (ch == 3 || ch == 13) {
  1169.                 *itemhit = diWorldShapeOK;
  1170.                 return TRUE;
  1171.             }
  1172.             break;
  1173.     }
  1174.     return FALSE;
  1175. }
  1176.  
  1177. /* This dialog asks for the shape and size of a world to generate randomly. */
  1178.  
  1179. world_shape_dialog()
  1180. {
  1181.     int done = FALSE, maybechanged = TRUE, changed = FALSE;
  1182.     Str255 dlgtext, tmpstr;
  1183.     short ditem;
  1184.     short itemtype;  Handle itemhandle;  Rect itemrect;
  1185.  
  1186.     if (worldshapewin == nil) {
  1187.         worldshapewin = GetNewDialog(dWorldShape, NULL, (DialogPtr) -1L);
  1188.         GetDItem(worldshapewin, diWorldShapePicture, &itemtype, &itemhandle, &itemrect);
  1189.         SetDItem(worldshapewin, diWorldShapePicture, itemtype, (Handle) draw_world_picture, &itemrect);
  1190.         /* Plug all the starting values into dialog items. */
  1191.         put_number_into_ditem(diWorldShapeCircumference, newcircumference);
  1192.         put_number_into_ditem(diWorldShapeWidth, newwidth);
  1193.         put_number_into_ditem(diWorldShapeHeight, newheight);
  1194.         put_number_into_ditem(diWorldShapeLatitude, newlatitude);
  1195.         put_number_into_ditem(diWorldShapeLongitude, newlongitude);
  1196.     }
  1197.     while (!done) {
  1198.         /* adjust items to reflect current status */
  1199.         if (maybechanged) {
  1200.             /* Dig the values out of the dialog boxes. */
  1201.             get_number_from_ditem(diWorldShapeCircumference, newcircumference);
  1202.             get_number_from_ditem(diWorldShapeWidth, newwidth);
  1203.             get_number_from_ditem(diWorldShapeHeight, newheight);
  1204.             get_number_from_ditem(diWorldShapeLatitude, newlatitude);
  1205.             get_number_from_ditem(diWorldShapeLongitude, newlongitude);
  1206.             DrawDialog(worldshapewin); /* just to do picture item */
  1207.             maybechanged = FALSE;
  1208.         }
  1209.         /* If the proposed shape is valid, enable the OK button. */
  1210.         /* (still need to disable returns etc) */
  1211.         GetDItem(worldshapewin, diWorldShapeOK, &itemtype, &itemhandle, &itemrect);
  1212.         HiliteControl((ControlHandle) itemhandle,
  1213.                       (valid_area_shape(newwidth, newheight, FALSE) ? 0 : 255));
  1214.         draw_default_button(worldshapewin, diWorldShapeOK);
  1215.         ModalDialog((ModalFilterProcPtr) filter_world_setup, &ditem);
  1216.         switch (ditem) {
  1217.             case diWorldShapeOK:
  1218.                 changed = TRUE;
  1219.                 /* Fall through to next case. */
  1220.             case diWorldShapeCancel:
  1221.                 done = TRUE;
  1222.                 break;
  1223.             case diWorldShapePicture:
  1224.             case diWorldShapeCircumference:
  1225.             case diWorldShapeWidth:
  1226.             case diWorldShapeHeight:
  1227.             case diWorldShapeLatitude:
  1228.             case diWorldShapeLongitude:
  1229.                 maybechanged = TRUE;
  1230.                 break;
  1231.             default:
  1232.                 break;
  1233.         }
  1234.     }
  1235.     DisposDialog(worldshapewin);
  1236.     worldshapewin = nil;
  1237.     return changed;
  1238. }
  1239.  
  1240. /* This is a callback that displays the list of sides and their assigned players. */
  1241.  
  1242. pascal void
  1243. draw_player_setup_list(win, ditem)
  1244. WindowPtr win;
  1245. short ditem;
  1246. {
  1247.     int i, advantage = -1;
  1248.     Rect entryrect, tmprect;
  1249.     char *sidetitle, playertitle[BUFSIZE];
  1250.     Player *player;
  1251.     Side *side;
  1252.     Str255 tmpstr;
  1253.     RgnHandle tmprgn;
  1254.     GrafPtr oldport;
  1255.     short itemtype;  Handle itemhandle;  Rect itemrect;
  1256.  
  1257.     GetDItem(playersetupwin, diPlayerSetupList, &itemtype, &itemhandle, &itemrect);
  1258.     tmprgn = NewRgn();
  1259.     GetClip(tmprgn);
  1260.     ClipRect(&itemrect);
  1261.     entryrect = itemrect;
  1262.     entryrect.bottom = entryrect.top + playerh;
  1263.     InsetRect(&entryrect, 1, 1);
  1264.     for (i = 0; i < numsides; ++i) {
  1265.         /* Clear this entry. */
  1266.         tmprect = entryrect;
  1267.         InsetRect(&tmprect, -1, -1);
  1268.         EraseRect(&tmprect);
  1269.         /* Describe the side. */
  1270.         side = assignments[i].side;
  1271.         draw_side_emblem(playersetupwin, entryrect.left + 2, entryrect.top + 2,
  1272.                          16, 16, side_number(side));
  1273.         sidetitle = (char *) short_side_title(side);
  1274.         if (sidetitle == NULL || strlen(sidetitle) == 0) sidetitle = "(unnamed)";
  1275.         MoveTo(entryrect.left + 2 + 16 + 2, entryrect.top + playerbaseline);
  1276.         TextFont(newYork);
  1277.         TextFace(0);
  1278.         DrawText(sidetitle, 0, strlen(sidetitle));
  1279.         /* Describe the intended player for the side. */
  1280.         player = assignments[i].player;
  1281.         long_player_title(playertitle, player, "mac");
  1282.         MoveTo(entryrect.left + 2 + 130, entryrect.top + playerbaseline);
  1283.         TextFace(italic);
  1284.         DrawText(playertitle, 0, strlen(playertitle));
  1285.         /* Indicate the player's initial advantage. */
  1286.         sprintf(spbuf, "%d", player->advantage);
  1287.         MoveTo(entryrect.left + 2 + 260, entryrect.top + playerbaseline);
  1288.         TextFace(0);
  1289.         DrawText(spbuf, 0, strlen(spbuf));
  1290.         /* (should be able to draw/update selection separately) */
  1291.         if (i == selectedplayer) {
  1292.             tmprect = entryrect;
  1293.             InsetRect(&tmprect, -1, -1);
  1294.             InvertRect(&tmprect);
  1295.             InsetRect(&tmprect, 3, 3);
  1296.             InvertRect(&tmprect);
  1297.         } else {
  1298.             /* Sides may be present, but not in the game; gray them out. */
  1299.             if (!side->ingame) gray_out_rect(&entryrect);
  1300.             FrameRect(&entryrect);
  1301.         }
  1302.         OffsetRect(&entryrect, 0, playerh);
  1303.     }
  1304.     /* Draw the remaining available positions as gray outlines. */
  1305.     PenPat(QD(gray));
  1306.     for (i = numsides + 1; i < g_sides_max(); ++i) {
  1307.         FrameRect(&entryrect);
  1308.         OffsetRect(&entryrect, 0, playerh);
  1309.     }
  1310.     PenNormal();
  1311.     SetClip(tmprgn);
  1312.     DisposeRgn(tmprgn);
  1313. }
  1314.  
  1315. pascal void
  1316. draw_player_setup_advantage(win, ditem)
  1317. WindowPtr win;
  1318. short ditem;
  1319. {
  1320.     int advantage, halfhgt;
  1321.     Side *side;
  1322.     short itemtype;  Handle itemhandle;  Rect itemrect;
  1323.  
  1324.     GetDItem(playersetupwin, diPlayerSetupAdvantage, &itemtype, &itemhandle, &itemrect);
  1325.     EraseRect(&itemrect);
  1326.     if (updownpicture != nil) {
  1327.         DrawPicture(updownpicture, &itemrect);
  1328.         if (!selected_a_player()) {
  1329.             gray_out_rect(&itemrect);
  1330.         } else {
  1331.             advantage = selected_player()->advantage;
  1332.             side = selected_side();
  1333.             halfhgt = (itemrect.bottom - itemrect.top) / 2;
  1334.             if (advantage <= side->minadvantage) {
  1335.                 /* Gray out the down arrow. */
  1336.                 itemrect.top += halfhgt;
  1337.                 gray_out_rect(&itemrect);
  1338.                 itemrect.top -= halfhgt;
  1339.             }
  1340.             if (side->maxadvantage > 0 && advantage >= side->maxadvantage) {
  1341.                 /* Gray out the up arrow. */
  1342.                 itemrect.bottom -= halfhgt;
  1343.                 gray_out_rect(&itemrect);
  1344.                 itemrect.bottom += halfhgt;
  1345.             }
  1346.         }
  1347.     }
  1348. }
  1349.  
  1350. pascal Boolean
  1351. filter_player_setup(dialog, evt, itemhit)
  1352. DialogPtr dialog;
  1353. EventRecord *evt;
  1354. short *itemhit;
  1355. {
  1356.     Player *tmpplayer, *curselected;
  1357.     GrafPtr oldport;
  1358.     Point pt;
  1359.     short ditem;
  1360.     char ch;
  1361.     int newadvantage;
  1362.     short itemtype;  Handle itemhandle;  Rect itemrect;
  1363.  
  1364.     switch (evt->what) {
  1365.         case mouseDown:
  1366.             GetPort(&oldport);
  1367.             SetPort(playersetupwin);
  1368.             pt = evt->where;
  1369.             GlobalToLocal(&pt);
  1370.             ditem = FindDItem(playersetupwin, pt) + 1;
  1371.             if (ditem == diPlayerSetupList) {
  1372.                 curselected = selected_player();
  1373.                 GetDItem(playersetupwin, diPlayerSetupList, &itemtype, &itemhandle, &itemrect);
  1374.                 select_player((pt.v - itemrect.top) / playerh);
  1375.                 if (curselected != selected_player()) {
  1376.                     set_player_setup_button_states();
  1377.                     DrawDialog(playersetupwin);
  1378.                 }
  1379.                 SetPort(oldport);
  1380.                 return TRUE;
  1381.             } else if (ditem == diPlayerSetupAdvantage) {
  1382.                 GetDItem(playersetupwin, diPlayerSetupAdvantage, &itemtype, &itemhandle, &itemrect);
  1383.                 if ((tmpplayer = selected_player()) != NULL) {
  1384.                     if (pt.v - itemrect.top < ((itemrect.bottom - itemrect.top) / 2)) {
  1385.                         adjust_advantage(tmpplayer, selected_side(), 1);
  1386.                     } else {
  1387.                         adjust_advantage(tmpplayer, selected_side(), -1);
  1388.                     }
  1389.                 }
  1390.             } else {
  1391.                 SetPort(oldport);
  1392.             }
  1393.             break;
  1394.         case keyDown:
  1395.             ch = evt->message & charCodeMask;
  1396.             if (ch == 3 || ch == 13) {
  1397.                 *itemhit = diPlayerSetupOK;
  1398.                 return TRUE;
  1399.             }
  1400.             /* Shortcuts to tweak the advantage. */
  1401.             if (ch == '+') {
  1402.                 adjust_advantage(selected_player(), selected_side(), 1);
  1403.             } else if (ch == '-') {
  1404.                 adjust_advantage(selected_player(), selected_side(), -1);
  1405.             }
  1406.             break;
  1407.     }
  1408.     return FALSE;
  1409. }
  1410.  
  1411. adjust_advantage(player, side, amt)
  1412. Player *player;
  1413. Side *side;
  1414. int amt;
  1415. {
  1416.     int newadvantage;
  1417.  
  1418.     if (player == NULL) return;
  1419.     newadvantage = player->advantage + amt;
  1420.     if (amt > 0 && side->maxadvantage > 0 && newadvantage > side->maxadvantage) {
  1421.         SysBeep(20);
  1422.         return;
  1423.     }
  1424.     if (amt < 0 && newadvantage < side->minadvantage) {
  1425.         SysBeep(20);
  1426.         return;
  1427.     }
  1428.     if (newadvantage != player->advantage) {
  1429.         player->advantage = newadvantage;
  1430.         DrawDialog(playersetupwin);
  1431.     }
  1432. }
  1433.  
  1434. select_player(n)
  1435. int n;
  1436. {
  1437.     if ((!between(0, n, numsides-1))
  1438.         || (assignments[n].side && !(assignments[n].side->ingame))) {
  1439.         n = -1;
  1440.     }
  1441.     selectedplayer = n;
  1442.     /* (update ditem directly?) */
  1443. }
  1444.  
  1445. /* Bring up a dialog that allows rearrangement of the side/player assignments.
  1446.    Returns true if accepted, false if cancelled. */
  1447.  
  1448. player_setup_dialog()
  1449. {
  1450.     int done = FALSE, playersok = FALSE, changedselected, changedall;
  1451.     short ditem;
  1452.     Player *tmpplayer;
  1453.     short itemtype;  Handle itemhandle;  Rect itemrect;
  1454.  
  1455.     if (playersetupwin == nil) {
  1456.         playersetupwin = GetNewDialog(dPlayerSetup, NULL, (DialogPtr) -1L);
  1457.         GetDItem(playersetupwin, diPlayerSetupList, &itemtype, &itemhandle, &itemrect);
  1458.         SetDItem(playersetupwin, diPlayerSetupList, itemtype, (Handle) draw_player_setup_list, &itemrect);
  1459.         GetDItem(playersetupwin, diPlayerSetupAdvantage, &itemtype, &itemhandle, &itemrect);
  1460.         SetDItem(playersetupwin, diPlayerSetupAdvantage, itemtype, (Handle) draw_player_setup_advantage, &itemrect);
  1461.         updownpicture = (PicHandle) GetResource('PICT', pUpDownPicture);
  1462.     }
  1463.     /* Always start out with the first player selected. */
  1464.     selectedplayer = 0;
  1465.     ShowWindow(playersetupwin);
  1466.     /* Stay in here until the player accepts the setup or cancels. */
  1467.     while (!done) {
  1468.         set_player_setup_button_states();
  1469.         draw_default_button(playersetupwin, diPlayerSetupOK);
  1470.         changedselected = changedall = FALSE;
  1471.         /* Sit in the modal dialog until something happens. */
  1472.         ModalDialog((ModalFilterProcPtr) filter_player_setup, &ditem);
  1473.         switch (ditem) {
  1474.             case diPlayerSetupOK:
  1475.                 playersok = TRUE;
  1476.                 /* Fall through. */
  1477.             case diPlayerSetupCancel:
  1478.                 done = TRUE;
  1479.                 break;
  1480.             case diPlayerSetupAdd:
  1481.                 add_side_and_player();
  1482.                 /* Select the newly-created side/player pair. */
  1483.                 select_player(numsides - 1);
  1484.                 init_emblem_images(selected_side());
  1485.                 changedall = TRUE;
  1486.                 break;
  1487.             case diPlayerSetupRemove:
  1488.                 remove_side_and_player();
  1489.                 break;
  1490.             case diPlayerSetupRename:
  1491.                 selected_side()->name = NULL;
  1492.                 selected_side()->noun = NULL;
  1493.                 /* always need to clear this cache before renaming... */
  1494.                 selected_side()->pluralnoun = NULL;
  1495.                 selected_side()->adjective = NULL;
  1496.                 make_up_side_name(selected_side());
  1497.                 init_emblem_images(selected_side());
  1498.                 changedselected = TRUE;
  1499.                 break;
  1500.             case diPlayerSetupAI:
  1501.                 tmpplayer = selected_player();
  1502.                 /* Click between mplayer and not, for now */
  1503.                 if (tmpplayer->aitypename) {
  1504.                     tmpplayer->aitypename = NULL;
  1505.                 } else {
  1506.                     tmpplayer->aitypename = "mplayer";
  1507.                 }
  1508.                 changedselected = TRUE;
  1509.                 break;
  1510.             case diPlayerSetupExchange:
  1511.                 /* Make the next player in the exchange be the selected one,
  1512.                    then multiple clicks on the exchange button will move the
  1513.                    selected player down through the list. */
  1514.                 selectedplayer = exchange_players(selectedplayer, -1);
  1515.                 changedall = TRUE;
  1516.                 break;
  1517.             default:
  1518.                 break;
  1519.         }
  1520.         if (changedall || changedselected) {
  1521.             /* (should minimize redrawing) */
  1522.             DrawDialog(playersetupwin);
  1523.         }
  1524.     }
  1525.     /* Get rid of this dialog, unlikely to need it again. */
  1526.     DisposDialog(playersetupwin);
  1527.     playersetupwin = nil;
  1528.     return playersok;
  1529. }
  1530.  
  1531. /* Set the state of the various buttons and controls.  In general, a side/player
  1532.    pair must be selected before anything can be done. */    
  1533.  
  1534. set_player_setup_button_states()
  1535. {
  1536.     int locked = g_player_sides_locked(), player = (selected_a_player() && !locked);
  1537.     int i, numingame = 0;
  1538.     Side *side;
  1539.     short itemtype;  Handle itemhandle;  Rect itemrect;
  1540.  
  1541.     for (i = 0; i < numsides; ++i) {
  1542.         if (assignments[i].side && (assignments[i].side)->ingame) ++numingame;
  1543.     }
  1544.  
  1545. #define set_player_button(di, val)  \
  1546.     GetDItem(playersetupwin, (di), &itemtype, &itemhandle, &itemrect);  \
  1547.     HiliteControl((ControlHandle) itemhandle, ((val) ? 0 : 255));
  1548.  
  1549.     set_player_button(diPlayerSetupAdd, !locked && numsides < g_sides_max());
  1550.     set_player_button(diPlayerSetupRemove, player && !locked && FALSE /* (numsides > g_sides_min()) */);
  1551.     set_player_button(diPlayerSetupRename, player && !(selected_side()->nameslocked));
  1552.     set_player_button(diPlayerSetupAI, player);
  1553.     set_player_button(diPlayerSetupRemote, player && FALSE);
  1554.     set_player_button(diPlayerSetupExchange, player && numingame > 1);
  1555. }
  1556.  
  1557. /* This dialog opens a player-specified file as a game. */
  1558.  
  1559. open_game_dialog()
  1560. {
  1561.     Point pnt;
  1562.     /* SFTypelist typelist; */
  1563.     long typelist[4];
  1564.     SFReply reply;
  1565.     char modulename[256];
  1566.     Module *module;
  1567.  
  1568.     /* Gotta be somewhere... */
  1569.     SetPt(&pnt, 100, 100);
  1570.     /* Game module are text files. */
  1571.     typelist[0] = 'TEXT';
  1572. #ifdef THINK_C
  1573.     SFGetFile(pnt, "\p", NULL, 1, &typelist, NULL, &reply);
  1574. #else
  1575. /*    SFGetFile(pnt, "\p", NULL, 1, (SFTypeList) &typelist, NULL, &reply); */
  1576. #endif
  1577.     if (reply.good) {
  1578.         return open_game_from_name_and_volume(reply.fName, reply.vRefNum);
  1579.     }
  1580.     return FALSE;
  1581. }
  1582.  
  1583. open_game_from_fsspec(fsspec)
  1584. FSSpec *fsspec;
  1585. {
  1586.     return open_game_from_name_and_volume(fsspec->name, fsspec->vRefNum);
  1587. }
  1588.  
  1589. open_game_from_name_and_volume(name, vrefnum)
  1590. Str255 name;
  1591. int vrefnum;
  1592. {
  1593.     char modulename[256];
  1594.     Module *module;
  1595.  
  1596.     toggle_debugging(&Debug);
  1597.     Dprintf("Name is %30s, vrefnum is %d\n", name+1, vrefnum);
  1598.     /* Set the current volume to be where the file is, so path-less
  1599.        fopen() in module loading below will find the file.  Note that this
  1600.        won't work for loading multiple modules from multiple non-library
  1601.        locations, but this dialog can only load one file at a time anyway. */
  1602.     SetVol(nil, vrefnum);
  1603.     clear_game_modules();
  1604.     /* Build a module with the given filename. */
  1605.     module = get_game_module(NULL);
  1606.     p2c(name, modulename);
  1607.     module->filename = copy_string(modulename);
  1608.     module->hook = NULL;
  1609.     /* This module is effectively the "selected game", whether or not
  1610.        it's loadable. */
  1611.     selectedgame = module;
  1612.     /* Load the module. */
  1613.     mainmodule = module;
  1614.     load_game_module(module);
  1615.     /* Change cursor back, in case it was different during loading. */
  1616.     SetCursor(&QD(arrow));
  1617.     if (!module->loaded) {
  1618.         init_warning("Could not load a module from %s", module->filename);
  1619.         return FALSE;
  1620.     }
  1621.     /* If the loaded module has bugs, we will get alerts here. */
  1622.     check_game_validity();
  1623.     /* We can now merge with the "new game" startup sequence. */
  1624.     return launch_game();
  1625. }
  1626.  
  1627. /* This is just the collection of init steps shared by both new and open dialogs. */
  1628.  
  1629. launch_game()
  1630. {
  1631.     /* Bring up the pre-player-setup variants. */
  1632.     if (!variants_dialog()) {
  1633.         /* Cancelling from variants dialog goes back to menu bar (?) */
  1634.         return FALSE;
  1635.     }
  1636.     /* Load any colors first, we can use them to substitute for patterns. */
  1637.     init_terrain_colors();
  1638.     /* Set up the images we'll be needing. */
  1639.     init_terrain_images();
  1640.     init_unit_images();
  1641.     /* Note that trial side/player assignment will create the default player if nec. */
  1642.     make_trial_assignments();
  1643.     init_all_emblem_images();
  1644.     /* Ask for modifications to the trial assignment. */
  1645.     if (!player_setup_dialog()) {
  1646.         /* Cancelled out of player setup, need to go around again. */
  1647.         update_new_game_list();
  1648.         return FALSE;
  1649.     }
  1650.     /* Complain if any images were missing.  Will also note if image resource files
  1651.        were never found - might be one of the explanations. */
  1652.     check_for_missing_images();
  1653.     /* A last-minute patch to ensure reasonable area dimensions. */
  1654.     if (area.width == 0) area.width = DEFAULTWIDTH;
  1655.     if (area.height == 0) area.height = DEFAULTHEIGHT;
  1656.     set_area_shape(area.width, area.height, TRUE);
  1657.     open_progress_dialog();
  1658.     /* Synthesize the remainder of the initial game setup. */
  1659.     run_synth_methods();
  1660.     final_init();
  1661.     assign_players_to_sides();
  1662.     /* No more interminable init calcs (we hope), so close the progress bar. */
  1663.     close_progress_dialog();
  1664.     /* Init the display now. */
  1665.     init_display();
  1666.     /* Do first set of turn calcs. */
  1667.     run_game(0);
  1668.     return TRUE;
  1669. }
  1670.  
  1671. /* This counts up all the images that had to be manufactured, and maybe puts up an
  1672.    alert asking if the player wants to go with the substitutes. */
  1673.  
  1674. check_for_missing_images()
  1675. {
  1676.     int u, t, e;
  1677.     Side *side;
  1678.     Str255 tmpstr;
  1679.  
  1680.     for_all_terrain_types(t) {
  1681.         if (timages[t].ersatz) record_missing_image(TTYP, t_type_name(t));
  1682.     }
  1683.     for_all_unit_types(u) {
  1684.         if (uimages[u].ersatz) record_missing_image(UTYP, u_type_name(u));
  1685.     }
  1686.     for_all_sides(side) {
  1687.         e = side_number(side);
  1688.         if (eimages[e].ersatz) record_missing_image(3, side_desig(side)); 
  1689.     }
  1690.     if (missing_images(tmpbuf)) {
  1691.         c2p(tmpbuf, tmpstr);
  1692.         ParamText(tmpstr, (foundimagesfile ? "\p" : "\p & no image resource files found"), "\p", "\p");
  1693.         switch (CautionAlert(aImagesMissing, nil)) {
  1694.             case 1:
  1695.                 /* We decided to live with low-quality images. */
  1696.                 break;
  1697.             case 2:
  1698.                 /* Ugliness is unbearable, get out now. */
  1699.                 /* (should do a more controlled exit...) */
  1700.                 ExitToShell();
  1701.                 break;
  1702.         }
  1703.     }
  1704. }
  1705.  
  1706. /* Rotate around "marching parentheses" cursors to indicate reading is happening. */
  1707.  
  1708. announce_read_progress()
  1709. {
  1710.     if (readprogressors[curcurs] != nil) SetCursor(*(readprogressors[curcurs]));
  1711.     curcurs = (curcurs + 1) % NUMcParens;
  1712. }
  1713.  
  1714. /* Display progress in lengthy initialization processes. */
  1715.  
  1716. /* This is the app-defined item that actually draws the progress bar. */
  1717.  
  1718. pascal void
  1719. draw_progress(win, ditem)
  1720. WindowPtr win;
  1721. short ditem;
  1722. {
  1723.     short itemtype;  Handle itemhandle;  Rect itemrect;
  1724.  
  1725.     GetDItem(progresswin, diProgressBar, &itemtype, &itemhandle, &itemrect);
  1726.     if (progress >= 0) {
  1727.         /* Only draw the gray bg initially or when "progress" decreases (would be a
  1728.            bug in synth method, but useful to get visual evidence if it happens). */
  1729.         if (lastprogress < 0 || lastprogress > progress) {
  1730.             FillRect(&itemrect, QD(gray));
  1731.             FrameRect(&itemrect);
  1732.         }
  1733.         itemrect.right =
  1734.             itemrect.left + (progress * (itemrect.right - itemrect.left)) / 100;
  1735.         FillRect(&itemrect, QD(black));
  1736.     } else {
  1737.         EraseRect(&itemrect);
  1738.     }
  1739. }
  1740.  
  1741. /* This starts off the lengthy process. */
  1742.  
  1743. open_progress_dialog()
  1744. {
  1745.     short itemtype;  Handle itemhandle;  Rect itemrect;
  1746.  
  1747.     progresswin = GetNewDialog(dProgress, NULL, (DialogPtr) -1L);
  1748.     GetDItem(progresswin, diProgressBar, &itemtype, &itemhandle, &itemrect);
  1749.     SetDItem(progresswin, diProgressBar, itemtype, (Handle) draw_progress, &itemrect);
  1750.     /* Always disable the Stop button for now. */
  1751.     /* (should figure out what to do with this) */
  1752.     GetDItem(progresswin, diProgressCancel, &itemtype, &itemhandle, &itemrect);
  1753.     HiliteControl((ControlHandle) itemhandle, 255);
  1754.     curcurs = 0;
  1755.     if (progressors[curcurs] != nil) SetCursor(*(progressors[curcurs]));
  1756. }
  1757.  
  1758. /* This is called to announce the beginning of a lengthy process. */
  1759.  
  1760. announce_lengthy_process(msg)
  1761. char *msg;
  1762. {
  1763.     Str255 tmpstr;
  1764.     short itemtype;  Handle itemhandle;  Rect itemrect;
  1765.  
  1766.     if (progresswin == nil) return;  /* be safe */
  1767.     /* Paste in the description of what is going to take so long. */
  1768.     GetDItem(progresswin, diProgressText, &itemtype, &itemhandle, &itemrect);
  1769.     c2p(msg, tmpstr);
  1770.     SetIText(itemhandle, tmpstr);
  1771.     /* Initialize the progress bar itself. */
  1772.     progress = -1;
  1773.     lastprogress = 0;
  1774.     ShowWindow(progresswin);
  1775.     SelectWindow(progresswin);
  1776.     Dprintf("%s;", msg);
  1777. #ifdef USE_CONSOLE
  1778.     if (Debug) fflush(stdout);
  1779. #endif
  1780.     curcurs = 0;
  1781.     if (progressors[curcurs] != nil) SetCursor(*(progressors[curcurs]));
  1782. }
  1783.  
  1784. /* This is called periodically to say how much progress has been made. */
  1785.  
  1786. announce_progress(pctdone)
  1787. int pctdone;
  1788. {
  1789.     if (progresswin == nil) return;  /* be safe */
  1790.     SelectWindow(progresswin);
  1791.     lastprogress = progress;
  1792.     progress = min(max(0, pctdone), 100);
  1793.     /* Redraw the progress bar app-defined item. */
  1794.     DrawDialog(progresswin);
  1795.     /* Switch to the next cursor in the animation sequence. */
  1796.     if (progressors[curcurs] != nil) SetCursor(*(progressors[curcurs]));
  1797.     curcurs = (curcurs + 1) % NUMcSynth;
  1798.     Dprintf(" %d%%", pctdone);
  1799. #ifdef USE_CONSOLE
  1800.     if (Debug) fflush(stdout);
  1801. #endif
  1802. }
  1803.  
  1804. /* This is called to signal the end of the lengthy process. */
  1805.  
  1806. finish_lengthy_process()
  1807. {
  1808.     short itemtype;  Handle itemhandle;  Rect itemrect;
  1809.  
  1810.     if (progresswin == nil) return;  /* be safe */
  1811.     SelectWindow(progresswin);
  1812.     /* Replace the text with a "reassuring" but uninformative message. */
  1813.     GetDItem(progresswin, diProgressText, &itemtype, &itemhandle, &itemrect);
  1814.     SetIText(itemhandle, "\pKeeping busy...");
  1815.     progress = -1;
  1816.     /* Force a redraw, which will cause the progress bar to be erased. */
  1817.     DrawDialog(progresswin);
  1818.     Dprintf(" done.\n");
  1819.     curcurs = 0;
  1820.     if (progressors[curcurs] != nil) SetCursor(*(progressors[curcurs]));
  1821. }
  1822.  
  1823. /* When we're completely done with all lengthy init processes, discard the dialog. */
  1824.  
  1825. close_progress_dialog()
  1826. {
  1827.     /* Restore the normal cursor. */
  1828.     SetCursor(&QD(arrow));
  1829.     DisposDialog(progresswin);
  1830.     progresswin = nil;
  1831. }
  1832.  
  1833. /* Kernel callback to put in a default player, assumed to be on
  1834.    "this" screen (which we call "mac", for lack of inspiration). */
  1835.  
  1836. Player *
  1837. add_default_player()
  1838. {
  1839.     Player *player = add_player();
  1840.     
  1841.     player->displayname = "mac";
  1842.     return player;
  1843. }
  1844.  
  1845. /* This is a kernel callback that sets the given side to have a user
  1846.    interface.  On the Mac, we just record the side as `dside', will
  1847.    finish opening later. */
  1848.  
  1849. init_ui(side)
  1850. Side *side;
  1851. {
  1852.     if (side != NULL) {
  1853.         side->ui = (UI *) xmalloc(sizeof(UI));
  1854.         side->ui->active = FALSE;
  1855.         dside = side;
  1856.         /* Make sure this is allocated.  Note that xmalloc clears, so this
  1857.            will look like it has an empty Pascal string. */
  1858.         if (curdatestr == NULL) curdatestr = (char *) xmalloc(50);
  1859.     }
  1860. }
  1861.  
  1862. /* Flag the screen as officially "open", init random globals, and open the
  1863.    initial set of windows. */
  1864.  
  1865. init_display()
  1866. {
  1867.     int p;
  1868.     Handle handle;
  1869.  
  1870.     /* If dside never got assigned, try some hasty repairs. */
  1871.     if (dside == NULL) {
  1872.         init_warning("Nobody wanted a display!");
  1873.         /* Make an arbitrary choice - pick the first side. */
  1874.         init_ui(side_n(1));
  1875.         if (dside == NULL) {
  1876.             /* Geez, nothing is going right. */
  1877.             init_error("Can't set up the display!");
  1878.         }
  1879.     }
  1880.     if (bordbitmaps == NULL) {
  1881.         bordbitmaps = (BitMap *) xmalloc(NUMPOWERS * sizeof(BitMap));
  1882.     }
  1883.     if (connbitmaps == NULL) {
  1884.         connbitmaps = (BitMap *) xmalloc(NUMPOWERS * sizeof(BitMap));
  1885.     }
  1886.     /* Init polygon-caching machinery. */
  1887.     for (p = 0; p < NUMPOWERS; ++p) {
  1888.         polygons[p] = nil;
  1889.         lastpolyx[p] = lastpolyy[p] = 0;
  1890.         cellrgns[p] = gridcellrgns[p] = nil;
  1891.         lastcellrgnx[p] = lastcellrgny[p] = 0;
  1892.         lastgridcellrgnx[p] = lastgridcellrgny[p] = 0;
  1893.         bordbitmaps[p].rowBytes = 0;
  1894.         connbitmaps[p].rowBytes = 0; 
  1895.     }
  1896.     if ((handle = GetResource('ICN#', 140)) != nil) {
  1897.         HLock(handle);
  1898.         bordbitmaps[4].baseAddr = *handle;
  1899.         bordbitmaps[4].rowBytes = 4;
  1900.         SetRect(&(bordbitmaps[4].bounds), 0, 0, 32, 32);
  1901.     }
  1902.     if ((handle = GetResource('ICN#', 141)) != nil) {
  1903.         HLock(handle);
  1904.         connbitmaps[4].baseAddr = *handle;
  1905.         connbitmaps[4].rowBytes = 4;
  1906.         SetRect(&(connbitmaps[4].bounds), 0, 0, 32, 32);
  1907.     }
  1908.     /* Init assorted list heads and window pointers. */
  1909.     maplist = NULL;
  1910.     listlist = NULL;
  1911.     unitcloseuplist = NULL;
  1912.     sidecloseuplist = NULL;
  1913.     historywin = nil;
  1914.     /* Flag the interface as ready to display stuff.  After this point,
  1915.        any events causing display activity will actually be rendered
  1916.        on the screen. */
  1917.     dside->ui->active = TRUE;
  1918.     /* Now bring up the initial collection of windows. */
  1919.     if (0 /* restore preferred windows */) {
  1920.     } else {
  1921.         /* The game window is useful orientation material. */
  1922.         create_game_window();
  1923.         ShowWindow(gamewin);
  1924.         /* This needs to come up so that players unfamiliar with the game
  1925.            will have some guidance. */
  1926.         instructions_dialog();
  1927.         /* This will be the main default display, so it should be on top. */
  1928.         create_map(5);
  1929.     }
  1930. #ifdef DESIGNERS
  1931.     /* If we're designing, bring up the palette automatically. */
  1932.     if (dside->designer) {
  1933.         create_design_window();
  1934.     }
  1935. #endif /* DESIGNERS */
  1936.     Dprintf("Opened Mac display!\n");
  1937. }
  1938.  
  1939. /* Set up the collection of terrain images/patterns, making a default pattern
  1940.    if necessary. */
  1941.  
  1942. init_terrain_images()
  1943. {
  1944.     int i, t;
  1945.     Handle pathandle;
  1946.     PicHandle pichandle;
  1947.     Image *img;
  1948.  
  1949.     timages = (ImageFamily *) xmalloc(numttypes * sizeof(ImageFamily));
  1950.  
  1951.     for_all_terrain_types(t) {
  1952.         init_image_family(&(timages[t]));
  1953.         /* (should be in kernel) */
  1954.         if (t_image_name(t) != NULL && strlen(t_image_name(t)) > 0) {
  1955.             timages[t].name = t_image_name(t);
  1956.         } else {
  1957.             timages[t].name = t_type_name(t);
  1958.         }
  1959.         load_image_family(&(timages[t]));
  1960.         /* Make a default pattern of box with ttype number in binary inside. */
  1961.         if (timages[t].numsizes == 0) {
  1962.             /* Count this type as not having a pattern, but only if we can't
  1963.                render the terrain with a solid color. */
  1964.             if (!(hasColorQD && t_color_defined(t))) {
  1965.                 timages[t].ersatz = TRUE;
  1966.             }
  1967.             /* Build a substitute pattern. */
  1968.             img = &(timages[t].images[(timages[t].numsizes)++]);
  1969.             img->w = img->h = 8;
  1970.             img->minw = img->minh = 1;
  1971.             img->maxw = img->maxh = 9999;
  1972.             img->pat[0] = 0x00;
  1973.             img->pat[1] = 0x7f;
  1974.             img->pat[2] = 0x41;
  1975.             img->pat[3] = 0x40 | (t << 1) | 0x01;
  1976.             img->pat[4] = 0x40 | (t << 1) | 0x01;
  1977.             img->pat[5] = 0x40 | (t << 1) | 0x01;
  1978.             img->pat[6] = 0x41;
  1979.             img->pat[7] = 0x7f;
  1980.             img->patdefined = TRUE;
  1981.         }
  1982.     }
  1983. }
  1984.  
  1985. interp_named_color(name, grayvar, colorvar)
  1986. char *name;
  1987. enum grays *grayvar;
  1988. RGBColor *colorvar;
  1989. {
  1990.     ImageColor tmpcolor;
  1991.  
  1992.     if (strcmp(name, "black") == 0) {
  1993.         *grayvar = blackgray;
  1994.         colorvar->red = colorvar->green = colorvar->blue = 0;
  1995.     } else if (strcmp(name, "dark gray") == 0) {
  1996.         *grayvar = mediumgray;
  1997.         colorvar->red = colorvar->green = colorvar->blue = 16384;
  1998.     } else if (strcmp(name, "gray") == 0) {
  1999.         *grayvar = mediumgray;
  2000.         colorvar->red = colorvar->green = colorvar->blue = 32768;
  2001.     } else if (strcmp(name, "light gray") == 0) {
  2002.         *grayvar = mediumgray;
  2003.         colorvar->red = colorvar->green = colorvar->blue = 49000;
  2004.     } else if (strcmp(name, "white") == 0) {
  2005.         *grayvar = whitegray;
  2006.         colorvar->red = colorvar->green = colorvar->blue = 65535;
  2007.     } else if (hasColorQD) {
  2008.         tmpcolor.name = name;
  2009.         load_image_color(&tmpcolor);
  2010.         colorvar->red = tmpcolor.r;
  2011.         colorvar->green =tmpcolor.g;
  2012.         colorvar->blue = tmpcolor.b;
  2013.     }
  2014. }
  2015.  
  2016. /* Allocate space and load a named color for each terrain type
  2017.    that defines one. */
  2018.  
  2019. init_terrain_colors()
  2020. {
  2021.     int t;
  2022.  
  2023.     if (hasColorQD) {
  2024.         tcolors = (ImageColor **) xmalloc(numttypes * sizeof(ImageColor *));
  2025.         for_all_terrain_types(t) {
  2026.             if (t_color(t) != NULL && strlen(t_color(t)) > 0) {
  2027.                 tcolors[t] = (ImageColor *) xmalloc(sizeof(ImageColor));
  2028.                 tcolors[t]->name = t_color(t);
  2029.                 load_image_color(tcolors[t]);
  2030.             }
  2031.         }
  2032.     }
  2033.     interp_named_color(g_grid_color(), &gridgray, &gridcolor);
  2034.     interp_named_color(g_unseen_color(), &unseengray, &unseencolor);
  2035.     if (strcmp(g_grid_color(), g_unseen_color()) == 0) gridmatchesunseen = TRUE;
  2036.     blackcolor.red = blackcolor.green = blackcolor.blue = 0;
  2037. }
  2038.  
  2039. /* Allocate and fill in images for all the unit types. */
  2040.  
  2041. init_unit_images()
  2042. {
  2043.     int u, i;
  2044.     Rect tmprect;
  2045.     Str255 tmpstr;
  2046.     Image *img;
  2047.     Handle sicnhandle, maskhandle;
  2048.     PicHandle pichandle;
  2049.     RgnHandle tmprgn;
  2050.  
  2051.     uimages = (ImageFamily *) xmalloc(numutypes * sizeof(ImageFamily));
  2052.  
  2053.     for_all_unit_types(u) {
  2054.         init_image_family(&(uimages[u]));
  2055.         /* (should be in kernel) */
  2056.         if (u_image_name(u) && strlen(u_image_name(u)) > 0) {
  2057.             uimages[u].name = u_image_name(u);
  2058.         } else {
  2059.             uimages[u].name = u_internal_name(u);
  2060.         }
  2061.         load_image_family(&(uimages[u]));
  2062.         /* Default unit image puts its name inside a box shape. */
  2063.         if (uimages[u].numsizes == 0) {
  2064.             uimages[u].ersatz = TRUE;
  2065.             img = &(uimages[u].images[(uimages[u].numsizes)++]);
  2066.             tmprgn = NewRgn();
  2067.             GetClip(tmprgn);
  2068.             SetRect(&tmprect, 0, 0, 32, 32);
  2069.             ClipRect(&tmprect);
  2070.             pichandle = OpenPicture(&tmprect);
  2071.             InsetRect(&tmprect, 2, 2);
  2072.             FillRect(&tmprect, QD(white));
  2073.             InsetRect(&tmprect, 1, 1);
  2074.             FrameRect(&tmprect);
  2075.             MoveTo(2, 16);
  2076.             DrawText(u_type_name(u), 0, strlen(u_type_name(u)));
  2077.             ClosePicture();
  2078.             SetClip(tmprgn);
  2079.             DisposeRgn(tmprgn);
  2080.             img->monopict = pichandle;
  2081.             img->istile = FALSE;
  2082.         }
  2083.     }
  2084. }
  2085.  
  2086. /* Load/create all the images of each side's emblem. */
  2087.  
  2088. init_all_emblem_images()
  2089. {
  2090.     Side *side2;
  2091.  
  2092.     eimages = (ImageFamily *) xmalloc((MAXSIDES + 1) * sizeof(ImageFamily));
  2093.  
  2094.     for_all_sides_plus_indep(side2) {
  2095.         init_emblem_images(side2);
  2096.     }
  2097. }
  2098.  
  2099. init_emblem_images(side2)
  2100. Side *side2;
  2101. {
  2102.     int e = side_number(side2), i;
  2103.     Rect tmprect;
  2104.     Str255 tmpstr;
  2105.     Image *img;
  2106.     Handle sicnhandle, maskhandle;
  2107.     PicHandle pichandle;
  2108.     RgnHandle tmprgn;
  2109.     
  2110.     init_image_family(&(eimages[e]));
  2111.     if (side2 != NULL && side2->emblemname && strlen(side2->emblemname) > 0) {
  2112.         eimages[e].name = side2->emblemname;
  2113.     }
  2114.     load_image_family(&(eimages[e]));
  2115.     /* Default image is SICN-sized number.  Use only if no images have been found
  2116.        and the emblem name is not "none". */
  2117.     if (eimages[e].numsizes == 0
  2118.         && !(eimages[e].name && strcmp(eimages[e].name, "none") == 0)) {
  2119.         img = &(eimages[e].images[(eimages[e].numsizes)++]);
  2120.         img->w = img->h = 16;
  2121.         img->minw = img->minh = 8;
  2122.         img->maxw = img->maxh = 64;
  2123.         sprintf(tmpbuf, "s%d", e);
  2124.         c2p(tmpbuf, tmpstr);
  2125.         if ((sicnhandle = GetNamedResource('SICN', tmpstr)) != nil) {
  2126.             /* Image itself is the first 32 bytes, mask is second 32 if present. */
  2127.             img->monosicn = sicnhandle;
  2128.             maskhandle = (Handle) NewHandle(32);
  2129.             if (SizeResource(sicnhandle) >= 64) {
  2130.                 for (i = 0; i < 32; ++i) {
  2131.                     (*maskhandle)[i] = (*sicnhandle)[i+32];
  2132.                 }
  2133.             } else {
  2134.                 /* Set up an all-white background. */
  2135.                 for (i = 0; i < 32; ++i) {
  2136.                     (*maskhandle)[i] = 0xff;
  2137.                 }
  2138.             }
  2139.             img->masksicn = maskhandle;
  2140.         } else {
  2141.             /* If we couldn't even find a number sicn, build a crude substitute pict. */
  2142.             eimages[e].ersatz = TRUE;
  2143.             tmprgn = NewRgn();
  2144.             GetClip(tmprgn);
  2145.             SetRect(&tmprect, 0, 0, 16, 16);
  2146.             ClipRect(&tmprect);
  2147.             pichandle = OpenPicture(&tmprect);
  2148.             FillRect(&tmprect, QD(white));
  2149.             MoveTo(2, 12);
  2150.             DrawText(tmpbuf+1, 0, strlen(tmpbuf+1));
  2151.             ClosePicture();
  2152.             SetClip(tmprgn);
  2153.             DisposeRgn(tmprgn);
  2154.             img->monopict = pichandle;
  2155.         }
  2156.         /* Emblems never tile. */
  2157.         img->istile = FALSE;
  2158.     }
  2159. }
  2160.